package com.xyy.saas.payment.payment.core.service;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.xyy.saas.payment.adpater.RouteManager;
import com.xyy.saas.payment.adpater.impl.PinganAdapter;
import com.xyy.saas.payment.api.impl.PaymentOrderApiImpl;
import com.xyy.saas.payment.callback.core.dto.CallbackMsg;
import com.xyy.saas.payment.callback.core.result.CallbackRunner;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.RocketMqTopic;
import com.xyy.saas.payment.common.constants.MqConstant;
import com.xyy.saas.payment.common.enums.AmountDirectionType;
import com.xyy.saas.payment.common.enums.AmountTypeEnum;
import com.xyy.saas.payment.common.enums.FrozenStatusEnum;
import com.xyy.saas.payment.common.enums.SettlementStatusEnum;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.bo.SeparateAccountsBo;
import com.xyy.saas.payment.cores.bo.UnfrozenBo;
import com.xyy.saas.payment.cores.bo.UserBo;
import com.xyy.saas.payment.cores.enums.*;
import com.xyy.saas.payment.cores.param.ProfitSharingParam;
import com.xyy.saas.payment.cores.vo.CallbackResultVo;
import com.xyy.saas.payment.cores.vo.ProfitSharingVo;
import com.xyy.saas.payment.cores.vo.ResultVO;
import com.xyy.saas.payment.cores.vo.SeparateAccountFromUserVo;
import com.xyy.saas.payment.dao.mapper.ProfitSharingDetailMapper;
import com.xyy.saas.payment.dao.mapper.ProfitSharingMapper;
import com.xyy.saas.payment.dao.model.*;
import com.xyy.saas.payment.merchant.core.dto.*;
import com.xyy.saas.payment.merchant.core.service.SeparateOrderService2;
import com.xyy.saas.payment.payment.core.adapter.AccountAdapter;
import com.xyy.saas.payment.payment.core.adapter.CheckParamAdapter;
import com.xyy.saas.payment.payment.core.adapter.TransformAdapter;
import com.xyy.saas.payment.service.PayAccountService;
import com.xyy.saas.payment.service.PayResultOrderService;
import com.xyy.saas.payment.service.SeparateOrderService;
import com.xyy.saas.payment.service.ThirdUserInfoService;
import com.xyy.saas.payment.settle.core.service.AccountService;
import com.xyy.saas.payment.trace.AlarmService;
import com.xyy.saas.payment.trace.DingTalkMessage;
import com.xyy.saas.payment.util.*;
import com.xyy.saas.payment.util.retry.RetryStrategy;
import com.xyy.saas.payment.util.retry.RetryTradeType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

@Service
@Slf4j
@DependsOn("apolloUtil")
public class ProfitSharingService {
    @Value("${profitSharing.settle.check.switch:true}")
    private Boolean settleCheckSwitch;
    @Value("${settleCreditPayV2.switch:false}")
    private Boolean settleCreditPayV2Switch;
    @Value("#{'${settleCreditPayV2Gray.payNos:}'.split(',')}")
    private List<String> settleCreditPayV2GrayPayNos;

    @Value("${allowRetryErrCode:ERR044}")
    private String allowRetryErrCode;
    @Value("${pingAnCreditSettleMaxRetryCount:60}")
    private Integer pingAnCreditSettleMaxRetryCount;
    /**  法定节假日（不包含普通周六日）  */
    @Value("#{'${holidays}'.split(',')}")
    private List<String> holidays;
    /**  调整上班的周六日  */
    @Value("#{'${work.weekends}'.split(',')}")
    private List<String> workWeekends;
    /**  星期六、星期日  */
    @Value("#{'${weekend.names}'.split(',')}")
    private List<String> weekendNames;

    @Resource
    private ProfitSharingMapper profitSharingMapper;
    @Resource
    private ProfitSharingDetailMapper profitSharingDetailMapper;
    @Resource
    private TransformAdapter transformAdapter;
    @Resource
    private PinganAdapter pinganAdapter;
    @Resource
    private CallbackRunner callbackRunner;
    @Resource
    private PaymentOrderApiImpl paymentOrderApi;
    @Resource
    private RouteManager routeManager;
    @Resource
    private AccountAdapter accountAdapter;
    @Resource
    private FrozenSettlementService frozenSettlementService;
    @Resource
    private LeafUtil leafUtil;
    @Autowired
    private AccountService accountService;
    @Resource
    private PayAccountService payAccountService;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private ThirdUserInfoService thirdUserInfoService;
    @Resource
    private SeparateOrderService2 separateOrderService2;
    @Resource
    private SeparateOrderService separateOrderService;
    @Resource
    private PayResultOrderService payResultOrderService;
    @Resource
    private RefundOrderService refundOrderService;
    @Autowired
    private AlarmService alarmService;

    @Value("${xydLoad.fundAccountId:TEST}")
    private String xydLoadFundAccountId;

    /**
     * 结算/分润
     * @param profitsharingParam
     * @return
     * @throws PaymentException
     */
    public ProfitSharingVo profitSharing(ProfitSharingParam profitsharingParam) throws PaymentException {
        log.info("ProfitSharingService#profitSharing start, profitsharingParam={}", JSONObject.toJSONString(profitsharingParam));

        // 1.参数校验
        CheckParamAdapter.checkProfitSharingParam(profitsharingParam);

        // 2.写入单据，将传入数据转换为ProfitSharing
        ProfitSharing profitSharing = transformAdapter.transformProfitSharing(profitsharingParam);
        // 分布式锁
        String key = RedisUtil.PREFIX_PROFIT_SHARING + profitSharing.getBusinessOrderNo();
        if(!redisUtil.setNx(key)) {
            throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_REPEAT_REQUEST);
        }

        try {
            // 幂等校验：验证结算汇总表状态
            idempotentCheck(profitsharingParam);
            // 将传入数据转换为分润详情
            List<ProfitSharingDetail> profitSharingDetail = transformAdapter.transformProfitSharingDetail(profitSharing.getProfitSharingNo(), profitsharingParam);

            // 支付渠道分组 条件为支付：key:channel value:sum(支付金额)
            Map<String, Integer> groupBySum = profitSharingDetail.stream().filter(detail -> OrderTypeEnum.PAY.getType().equals(detail.getOrderType())).
                    collect(Collectors.groupingBy(ProfitSharingDetail::getPayChannel,
                            Collectors.reducing(0, ProfitSharingDetail::getOnlinePayAmount, Integer::sum)));
            groupBySum.put(Constants.CHANNEL_BALANCE_NAME, profitSharingDetail.stream().filter(detail -> OrderTypeEnum.PAY.getType().equals(detail.getOrderType())).
                    mapToInt(ProfitSharingDetail::getBalancePayAmount).sum());
            // {"pingan_alipay":445752,"pingan_wx":1081670,"direct_wx":22340,"balance_pay":20450}
            profitSharing.setChannelGroupAmount(JSONObject.toJSONString(groupBySum));
            Integer totalPayAmount = groupBySum.values().stream().mapToInt(Integer::intValue).sum();

            // 退款金额
            Integer totalBalanceRefundAmount = profitSharingDetail.stream().
                    filter(detail -> OrderTypeEnum.REFUND.getType().equals(detail.getOrderType())).mapToInt(ProfitSharingDetail::getBalancePayAmount).sum();
            Integer totalOnlineRefundAmount = profitSharingDetail.stream().
                    filter(detail -> OrderTypeEnum.REFUND.getType().equals(detail.getOrderType())).mapToInt(ProfitSharingDetail::getOnlinePayAmount).sum();
            Integer totalRefundAmount = totalOnlineRefundAmount + totalBalanceRefundAmount;
            profitSharing.setTotalBalanceRefundAmount(totalBalanceRefundAmount);
            profitSharing.setTotalOnlineRefundAmount(totalOnlineRefundAmount);
            profitSharing.setTotalRefundAmount(totalRefundAmount);
            log.info("ProfitSharingService#profitSharing profitSharing={}", JSONObject.toJSONString(profitSharing));

            /**
             * 金额均带符号参与计算
             * 1.入账单实付金额=订单的实付金额+退款单的实付金额（就是实际退款金额）
             * 2.入账单结算金额=入账单实付金额+入账单平台优惠（就是平台补贴金额）-佣金（平台抽佣的金额）
             */
            if(!profitsharingParam.getTotalPayAmount().equals(totalPayAmount + totalRefundAmount)) {
                throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_PAY_AMOUNT_ERROR);
            }

            if(!profitsharingParam.getTotalSettleAmount().
                    equals(profitsharingParam.getTotalPayAmount() + profitsharingParam.getTotalPlatformDiscountAmount() - profitsharingParam.getTotalBrokerageAmount()))
            {
                throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_SETTLE_AMOUNT_ERROR);
            }

            profitSharingMapper.insert(profitSharing);
            profitSharingDetailMapper.batchInsert(profitSharingDetail);
        }
        finally {
            redisUtil.del(key);
        }

        // 4.异步分润
        log.info("分润标识为处理分润，分润单号:{}", profitSharing.getProfitSharingNo());
        callbackRunner.syncSendMsg(RocketMqTopic.TOPIC_PROFIT_SHARING__CALLBACK, profitSharing);

        // 5.返回业务
        return ProfitSharingVo.build(profitSharing.getProfitSharingNo(), profitSharing.getBusinessOrderNo());
    }
    /**
     * 是否可以结算：节假日或周六日不进行结算
     *
     * @return
     */
//    private boolean isCanSettle() {
//        Date date = new Date();
//        String dateStr = DateUtil.date2String(date,DateUtil.DATEFORMATDAY);
//        //是否节假日
//        if (!CollectionUtils.isEmpty(holidays)) {
//            if (holidays.contains(dateStr)) {
//                return false;
//            }
//        }
//        //是否周六日
//        String week = DateUtil.getWeek(date);
//
//        if (!CollectionUtils.isEmpty(weekendNames) && weekendNames.contains(week)
//                && (CollectionUtils.isEmpty(workWeekends) ||!workWeekends.contains(dateStr))) {
//            return false;
//        }
//        return true;
//    }
    /**
     * 异步处理结算/分润 消费 RocketMqTopic.TOPIC_PROFIT_SHARING__CALLBACK 消息
     */
    public void profitSharing(ProfitSharing profitSharing) {
        // 获取用户子账户信息
        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(profitSharing.getAccountId());
        if(Constants.CHANNEL_PINGAN_NAME.equals(thirdUserInfo.getThirdType())) {
            profitSharingByPingan(profitSharing);
        }
        else {
            profitSharingByFBank(profitSharing);
        }
    }

    /**
     * 平安结算/分润
     */
    private void profitSharingByPingan(ProfitSharing profitSharing) {
        String key = RedisUtil.PREFIX_PROFIT_SHARING + profitSharing.getAccountId();
        String withdrawKey = RedisUtil.D_AMOUNT_LOCK + profitSharing.getAccountId();

        try  {
            if(!redisUtil.setNx(key)) {
                log.error("ProfitSharingService#profitSharingByPingan error[Repeat request], profitSharing={}", JSONObject.toJSONString(profitSharing));
                // 延迟10m重试
                callbackRunner.syncSendDelayMsg(RocketMqTopic.TOPIC_PROFIT_SHARING__CALLBACK, profitSharing, MqConstant.DELAY_LEVELS[3]);
                return;
            }
            // 查询分润信息，若未查询到则延迟消费
            final ProfitSharing curProfitSharing = profitSharingMapper.queryByProfitSharingNo(profitSharing.getProfitSharingNo());
            if (curProfitSharing == null) {
                callbackRunner.syncSendDelayMsg(RocketMqTopic.TOPIC_PROFIT_SHARING__CALLBACK, profitSharing, MqConstant.DELAY_LEVELS[3]);
                return;
            }
            // 若分润信息已处理成功，则直接返回
            if (SettlementStatusEnum.SUCCESS.getStatus().equals(curProfitSharing.getStatus())) {
                return;
            }
            // 0.查询结算明细
            ProfitSharingDetail param = new ProfitSharingDetail();
            param.setProfitSharingNo(profitSharing.getProfitSharingNo());
            param.setStatus(SettlementStatusEnum.PENDING.getStatus());
            List<ProfitSharingDetail> profitSharingDetails = profitSharingDetailMapper.selectByParam(param);

            // 支付数据
            List<ProfitSharingDetail> payProfitSharingDetails = profitSharingDetails.stream().
                    filter(detail -> OrderTypeEnum.PAY.getType().equals(detail.getOrderType())).
                    collect(Collectors.toList());

            // 退款数据
            List<ProfitSharingDetail> refundProfitSharingDetails = profitSharingDetails.stream().
                    filter(detail -> OrderTypeEnum.REFUND.getType().equals(detail.getOrderType())).
                    collect(Collectors.toList());

            // 1.校验订单平安是否已全部清分，获取清算日期
            String latelyTradeDate = getLatelyTradeDateFromProfitSharingDetail(payProfitSharingDetails, DateUtil.DATEFORMATDAY_02);

            // 无正向交易，PinganMethod.KFEJZB6108 接口获取清算数据，验证是否均清算完成
            if(StringUtils.isNotBlank(latelyTradeDate)) {
                QueryChannelClearVo queryChannelClearVo = routeManager.queryChannelClear(QueryChannelClearDto.builder().
                        businessIdType(profitSharing.getBusinessOrderType()).
                        accountId(profitSharing.getAccountId()).
                        startDate(latelyTradeDate).
                        endDate(latelyTradeDate).
                        build());
                // 所有交易 "0".equals(item.getClearStatus())
                if(!queryChannelClearVo.isClearFinish()) {
                    throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_SETTLE_CONTAIN_NOT_CLEARING_ORDER);
                }
            }

            // 提现锁，可重入无需判断加锁返回值
            redisUtil.setNx(withdrawKey);

            // 3.结算
            /**
             * 3.1 支付
             * 富民渠道：挂账
             * 平安渠道：分账
             * 直连渠道：富民降级：挂账，平安降级：分账
             * 购物金：转账-9
             */
            for(ProfitSharingDetail item : payProfitSharingDetails) {
                boolean unSupportChannelException = true;

                // 3.1.1 富民渠道：挂账
                if(item.getPayChannel().contains(Constants.CHANNEL_FUMIN_BANK_NAME)) {
                    separateOrderService2.registerOrder(RegisterOrderDto.builder().
                        businessIdType(profitSharing.getBusinessOrderType()).
                        accountId(profitSharing.getAccountId()).
                        businessOrderNo(item.getPaymentOrderNo()).
                        amount(item.getOnlinePayAmount()).build());
                    unSupportChannelException = false;
                }

                if (item.getPayChannel().contains(Constants.CHANNEL_XYD_LOAN)) {
                    popXydCreditPayProfitSharing(profitSharing,item);
                    unSupportChannelException = false;
                }

                // 3.1.2 平安渠道：分账
                if(item.getPayChannel().contains(Constants.CHANNEL_PINGAN_NAME)) {
                    settle(profitSharing, item);
                    unSupportChannelException = false;
                }

                // 3.1.3 直连渠道：富民降级：挂账，平安降级：分账，京东渠道走平安降级的分账
                if(item.getPayChannel().contains(Constants.CHANNEL_DIRECT_NAME) || Constants.thirdChannelSubmitPingan(item.getPayChannel())) {
                    PayResultOrder resultOrder = payResultOrderService.selectByPayNoAndChannel(item.getPaymentOrderNo(), item.getPayChannel());

                    // 富民降级：挂账，京东渠道走平安降级分账
                    if(PayResultOrder.ROYALTY_STATUS_NONE.equals(resultOrder.getRoyalty())) {
                        separateOrderService2.registerOrder(RegisterOrderDto.builder().
                            businessIdType(profitSharing.getBusinessOrderType()).
                            accountId(profitSharing.getAccountId()).
                            businessOrderNo(item.getPaymentOrderNo()).
                            amount(item.getOnlinePayAmount()).build());
                    }
                    // 平安降级：分账
                    else {
                        settle(profitSharing, item);
                    }

                    unSupportChannelException = false;
                }

                // 3.1.4 购物金：转账-9，渠道为余额支付渠道或者余额支付金额 != 0
                if(item.getPayChannel().contains(Constants.CHANNEL_BALANCE_NAME) || !NumberUtils.INTEGER_ZERO.equals(item.getBalancePayAmount())) {
                    separateOrderService2.accountTransfer(AccountTransferDto.builder().
                        transferType(AccountTransferDto.TRANSFER_PAY).
                        businessIdType(profitSharing.getBusinessOrderType()).
                        outAccountId(ApolloUtil.getProperty(Constants.EC_PINGAN_BALANCE_ACCOUNT_ID)).
                        inAccountId(profitSharing.getAccountId()).
                        amount(item.getBalancePayAmount()).
                        businessOrderNo(item.getOrderNo()).
                        remark(item.getOrderNo() + "|" + "购物金-支付结算").build());
                    unSupportChannelException = false;
                }

                // 不支持的渠道抛异常
                if(unSupportChannelException) {
                    throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_SETTLE_NOT_SUPPORT_CHANNEL);
                }
                item.setStatus(SettlementStatusEnum.SUCCESS.getStatus());
                profitSharingDetailMapper.updateStatusById(item);
            }

            /**
             * 3.2 平台补贴
             */
            boolean delayHandlePlatformDiscount = Boolean.FALSE;
            // 平台总优惠 > 0
            if(profitSharing.getTotalPlatformDiscountAmount() > NumberUtils.INTEGER_ZERO) {
                separateOrderService2.accountTransfer(AccountTransferDto.builder().
                    transferType(AccountTransferDto.TRANSFER_PAY).
                    businessIdType(profitSharing.getBusinessOrderType()).
                    outAccountId(ApolloUtil.getProperty(Constants.EC_PINGAN_MARKET_ACCOUNT_ID)).
                    inAccountId(profitSharing.getAccountId()).
                    amount(profitSharing.getTotalPlatformDiscountAmount()).
                    businessOrderNo(profitSharing.getBusinessOrderNo() + "_" + AmountTypeEnum.PLATFORM_DISCOUNT_AMOUNT.name()).build());
            }
            else if(profitSharing.getTotalPlatformDiscountAmount() < NumberUtils.INTEGER_ZERO) {
                delayHandlePlatformDiscount = Boolean.TRUE;
            }

            /**
             * 3.3 平台抽佣
             */
            if(profitSharing.getTotalBrokerageAmount() > NumberUtils.INTEGER_ZERO) {
                // 佣金金额 > 0：商家账户 -> 平安收佣金账户 (佣金金额)
                separateOrderService2.accountTransfer(AccountTransferDto.builder().
                    transferType(AccountTransferDto.TRANSFER_REFUND).
                    businessIdType(profitSharing.getBusinessOrderType()).
                    outAccountId(profitSharing.getAccountId()).
                    inAccountId(ApolloUtil.getProperty(Constants.EC_PINGAN_IN_BROKERAGE_ACCOUNT_ID)).
                    amount(profitSharing.getTotalBrokerageAmount()).
                    businessOrderNo(profitSharing.getBusinessOrderNo() + "_" + AmountTypeEnum.BROKERAGE_AMOUNT.name()).build());
            }
            else if(profitSharing.getTotalBrokerageAmount() < NumberUtils.INTEGER_ZERO) {
                // 佣金金额 < 0：平安收佣金账户 -> 商家账户 (佣金金额)
                separateOrderService2.accountTransfer(AccountTransferDto.builder().
                    transferType(AccountTransferDto.TRANSFER_PAY).
                    businessIdType(profitSharing.getBusinessOrderType()).
                    outAccountId(ApolloUtil.getProperty(Constants.EC_PINGAN_OUT_BROKERAGE_ACCOUNT_ID)).
                    inAccountId(profitSharing.getAccountId()).
                    amount(Math.abs(profitSharing.getTotalBrokerageAmount())).
                    businessOrderNo(profitSharing.getBusinessOrderNo() + "_" + AmountTypeEnum.BROKERAGE_AMOUNT.name()).build());
            }

            // 营销负数延后处理
            if(delayHandlePlatformDiscount) {
                separateOrderService2.accountTransfer(AccountTransferDto.builder().
                    transferType(AccountTransferDto.TRANSFER_REFUND).
                    businessIdType(profitSharing.getBusinessOrderType()).
                    outAccountId(profitSharing.getAccountId()).
                    inAccountId(ApolloUtil.getProperty(Constants.EC_PINGAN_MARKET_ACCOUNT_ID)).
                    amount(Math.abs(profitSharing.getTotalPlatformDiscountAmount())).
                    businessOrderNo(profitSharing.getBusinessOrderNo() + "_" + AmountTypeEnum.PLATFORM_DISCOUNT_AMOUNT.name()).build());
            }

            try {
                log.info("退款分润前,停止五秒，防止分账撤销失败:{}", profitSharing.getProfitSharingNo());
                Thread.sleep(5 * 1000L);
            } catch (InterruptedException e) { }

            /**
             * 3.4 退款
             * 富民渠道：转账-7
             * 平安渠道：转账撤销
             * 直连渠道：富民降级：转账-7，平安降级：转账撤销
             * 购物金：转账-7
             */
            for(ProfitSharingDetail item : refundProfitSharingDetails) {
                boolean unSupportChannelException = true;

                // 富民渠道：转账-7
                if(item.getPayChannel().contains(Constants.CHANNEL_FUMIN_BANK_NAME)) {
                    separateOrderService2.accountTransfer(AccountTransferDto.builder().
                        transferType(AccountTransferDto.TRANSFER_REFUND).
                        businessIdType(profitSharing.getBusinessOrderType()).
                        outAccountId(profitSharing.getAccountId()).
                        inAccountId(ApolloUtil.getProperty(Constants.EC_PINGAN_REFUND_ACCOUNT_ID)).
                        amount(Math.abs(item.getOnlinePayAmount())).
                        businessOrderNo(item.getPaymentOrderNo()).
                        remark(item.getOrderNo()).build());
                    unSupportChannelException = false;
                }

                // 平安渠道：转账撤销
                if(item.getPayChannel().contains(Constants.CHANNEL_PINGAN_NAME)) {
                    separateOrderService2.accountTransferRefund(AccountTransferRefundDto.builder().
                        businessIdType(profitSharing.getBusinessOrderType()).
                        amount(Math.abs(item.getOnlinePayAmount())).
                        businessOrderNo(item.getPaymentOrderNo()).build());
                    unSupportChannelException = false;
                }

                if (item.getPayChannel().contains(Constants.CHANNEL_XYD_LOAN)) {
                    popXydCreditRefundProfitSharing(profitSharing,item);
                    unSupportChannelException = false;
                }

                // 直连渠道：富民降级：转账-7，平安降级：转账撤销，京东支付走平安的转账撤销
                if(item.getPayChannel().contains(Constants.CHANNEL_DIRECT_NAME) || Constants.thirdChannelSubmitPingan(item.getPayChannel())) {
                    RefundOrder refundOrder = refundOrderService.selectByRefundNo(item.getPaymentOrderNo());
                    PayResultOrder resultOrder = payResultOrderService.selectByPayNoAndChannel(refundOrder.getPayNo(), refundOrder.getChannel());

                    // 富民降级：转账-7，京东支付走平安的转账撤销
                    if(PayResultOrder.ROYALTY_STATUS_NONE.equals(resultOrder.getRoyalty())) {
                        separateOrderService2.accountTransfer(AccountTransferDto.builder().
                            transferType(AccountTransferDto.TRANSFER_REFUND).
                            businessIdType(profitSharing.getBusinessOrderType()).
                            outAccountId(profitSharing.getAccountId()).
                            inAccountId(ApolloUtil.getProperty(Constants.EC_PINGAN_REFUND_ACCOUNT_ID)).
                            amount(Math.abs(item.getOnlinePayAmount())).
                            businessOrderNo(item.getPaymentOrderNo()).
                            remark(item.getOrderNo()).build());
                    }
                    else {
                        // 平安降级：转账撤销：KFEJZB6164：查询原支付单分账数据后走会员间交易退款
                        separateOrderService2.accountTransferRefund(AccountTransferRefundDto.builder().
                            businessIdType(profitSharing.getBusinessOrderType()).
                            amount(Math.abs(item.getOnlinePayAmount())).
                            businessOrderNo(item.getPaymentOrderNo()).build());
                    }

                    unSupportChannelException = false;
                }

                // 购物金：转账-7
                if(item.getPayChannel().contains(Constants.CHANNEL_BALANCE_NAME) || !NumberUtils.INTEGER_ZERO.equals(item.getBalancePayAmount())) {
                    separateOrderService2.accountTransfer(AccountTransferDto.builder().
                        transferType(AccountTransferDto.TRANSFER_REFUND).
                        businessIdType(profitSharing.getBusinessOrderType()).
                        outAccountId(profitSharing.getAccountId()).
                        inAccountId(ApolloUtil.getProperty(Constants.EC_PINGAN_BALANCE_ACCOUNT_ID)).
                        amount(Math.abs(item.getBalancePayAmount())).
                        businessOrderNo(item.getOrderNo()).
                        remark(item.getOrderNo() + "|" + "购物金-退款结算").build());
                    unSupportChannelException = false;
                }

                // 不支持的渠道抛异常
                if(unSupportChannelException) {
                    throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_SETTLE_NOT_SUPPORT_CHANNEL);
                }
                item.setStatus(SettlementStatusEnum.SUCCESS.getStatus());
                profitSharingDetailMapper.updateStatusById(item);
            }

            // 3.更新单据
            profitSharing.setStatus(SettlementStatusEnum.SUCCESS.getStatus());
            profitSharing.setMessage(SettlementStatusEnum.SUCCESS.getDesc());
            profitSharingMapper.updateByPrimaryKeySelective(profitSharing);

            // 4.释放提现锁
            redisUtil.del(withdrawKey);

            // 5.业务回调
            callbackRunner.callback(CallbackMsg.build(CallbackResultVo.PROFIT_SHARING_SUCCESS, profitSharing.getCallback(),
                    profitSharing.getProfitSharingNo(), profitSharing.getBusinessOrderNo(), ResultCodeEnum.SUCCESS.getDisplayMsg(), getCallBackExtend(profitSharing)));
        }
        catch(Exception ex) {
            log.error("profitSharing error {}" ,profitSharing.getProfitSharingNo(), ex);
            String errorMsg = ex instanceof PaymentException ? ((PaymentException) ex).getErrMsg() : StringUtils.EMPTY;
            errorMsg = StringUtils.isNotBlank(errorMsg) ? errorMsg : SettlementStatusEnum.FAIL.getDesc();

            // 4.更新单据
            profitSharing.setStatus(SettlementStatusEnum.FAIL.getStatus());
            profitSharing.setMessage(errorMsg);
            profitSharingMapper.updateByPrimaryKeySelective(profitSharing);

            if (ex instanceof PaymentException) {
                String errCode = ((PaymentException) ex).getErrCode();
                allowRetryErrCode = allowRetryErrCode + "," + ResultCodeEnum.PROFITSHARING_BUSY.getCode();
                if (StringUtils.isNotEmpty(errCode) && allowRetryErrCode.contains(errCode)) {
                    long retryCount = redisUtil.incr("retryProfitSharing", profitSharing.getProfitSharingNo());
                    redisUtil.set("retryProfitSharing"+profitSharing.getProfitSharingNo(), 60 * 60 * 3);
                    log.warn("profitSharingByPingan retryCount:{}, profitSharing={}",retryCount, profitSharing.getProfitSharingNo());
                    if (retryCount < 30) {
                        if (ResultCodeEnum.PROFIT_SHARING_SETTLE_CONTAIN_NOT_CLEARING_ORDER.getCode().equals(errCode)) {
                            // 平安未清算：延迟30分钟
                            log.info("重试分润原因为平安未清算，延迟30分钟重试:{}", profitSharing.getProfitSharingNo());
                            callbackRunner.syncSendDelayMsg(RocketMqTopic.TOPIC_PROFIT_SHARING__CALLBACK, profitSharing, MqConstant.DELAY_LEVELS[4]);
                        } else {
                            // 其他异常：延迟10m重试
                            log.info("重试分润原因为其它异常:{}，延迟10分钟重试:{}", errCode, profitSharing.getProfitSharingNo());
                            callbackRunner.syncSendDelayMsg(RocketMqTopic.TOPIC_PROFIT_SHARING__CALLBACK, profitSharing, MqConstant.DELAY_LEVELS[3]);
                        }
                        return;
                    } else {
                        log.warn("重试分润次数已达上限:{}", profitSharing.getProfitSharingNo());
                        DingTalkMessage msg = DingTalkMessage.builder()
                                .type("平安分润失败")
                                .method("ProfitSharingService.profitSharingByPingan")
                                .msg("重试次数" + retryCount + "已达上限," + errorMsg)
                                .param(JSON.toJSONString(profitSharing)).build();
                        alarmService.alarm(msg);
                    }
                } else {
                    log.warn("PaymentException errCode:{},errCode:{}", profitSharing.getProfitSharingNo(), errCode);
                    DingTalkMessage msg = DingTalkMessage.builder()
                            .type("平安分润失败")
                            .method("ProfitSharingService.profitSharingByPingan")
                            .msg("不允许重试," + "errCode:" + errCode + "," + "errorMsg:" + errorMsg)
                            .param(JSON.toJSONString(profitSharing)).build();
                    alarmService.alarm(msg);
                }

            } else {
                log.warn("is not PaymentException:{}", profitSharing.getProfitSharingNo());
                DingTalkMessage msg = DingTalkMessage.builder()
                        .type("平安分润失败")
                        .method("ProfitSharingService.profitSharingByPingan")
                        .msg("其他异常," + "errorMsg:" + errorMsg)
                        .param(JSON.toJSONString(profitSharing)).build();
                alarmService.alarm(msg);
            }
            // 5.业务回调
            callbackRunner.callback(CallbackMsg.build(CallbackResultVo.PROFIT_SHARING_FAIL, profitSharing.getCallback(),
                    profitSharing.getProfitSharingNo(), profitSharing.getBusinessOrderNo(), errorMsg, getCallBackExtend(profitSharing)));
        }
        finally {
            redisUtil.del(key);
        }
    }

    private void popXydCreditRefundProfitSharing(ProfitSharing profitSharing, ProfitSharingDetail item) throws PaymentException {
        log.info("###小雨点退款分润 popXydCreditRefundProfitSharing start, {}-{}", profitSharing.getProfitSharingNo(), item.getPaymentOrderNo());

        /*
         以下两个条件必须满足其一：
         1）有待退款回补平台佣金户的记录
         2）有退款垫资记录（走小雨点线上退款不会垫资）
         */

        // 待退款回补平台佣金户的记录
        SeparateOrder returnBrokerageSeparateOrder = separateOrderService.queryOneByBusinessOrderNo(item.getPaymentOrderNo() + "_" + AmountTypeEnum.XYD_CREDIT_REFUND_RETURN_BROKERAGE_AMOUNT.name());
        // 退款垫资记录
        List<SeparateOrder> advanceSeparateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(item.getPaymentOrderNo());

        log.info("###小雨点退款分润 {}-{} returnBrokerageSeparateOrder:{}, advanceSeparateOrders:{}", profitSharing.getProfitSharingNo(), item.getPaymentOrderNo(),
                JSON.toJSONString(returnBrokerageSeparateOrder), JSON.toJSONString(advanceSeparateOrders));

        boolean canRefundProfitSharing = returnBrokerageSeparateOrder != null || CollectionUtil.isNotEmpty(advanceSeparateOrders);

        if (!canRefundProfitSharing) {
            log.error("###小雨点退款分润 前置条件不满足, {}-{}", profitSharing.getProfitSharingNo(), item.getPaymentOrderNo());
            throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR, "小雨点退款分账时" + item.getPaymentOrderNo() + "垫资或退款回补 separateOrder 数据不存在");
        }

        if (returnBrokerageSeparateOrder != null) {
            // 回补退款到平安收佣金户
            AccountTransferDto xydLoanFundPayAcctTransDto = AccountTransferDto.builder().
                    transferType(AccountTransferDto.TRANSFER_REFUND).
                    businessIdType(profitSharing.getBusinessOrderType()).
                    outAccountId(returnBrokerageSeparateOrder.getCustMerchant()).
                    inAccountId(returnBrokerageSeparateOrder.getCustNoIn()).
                    amount(returnBrokerageSeparateOrder.getTradeAmount()).
                    businessOrderNo(returnBrokerageSeparateOrder.getBusinessOrderNo()).build();
            RetryStrategy.RETRY_2_ATTEMPTS.executeWithRetry(() -> pinganAdapter.asyncAccountTransferAndPoll(xydLoanFundPayAcctTransDto), RetryTradeType.XYD_CREDIT_REFUND_PROFITSHARING, true);
        }

        if (CollectionUtil.isNotEmpty(advanceSeparateOrders)) {
            // 退款垫资回补
            separateOrderService2.accountTransferRefund(AccountTransferRefundDto.builder().
                    businessIdType(profitSharing.getBusinessOrderType()).
                    amount(Math.abs(item.getOnlinePayAmount())).
                    businessOrderNo(item.getPaymentOrderNo()).build());
        }

    }

    private void popXydCreditPayProfitSharing(ProfitSharing profitSharing, ProfitSharingDetail item) throws PaymentException {
        log.info("###小雨点支付分润 popXydCreditPayProfitSharing start, profitSharingNo:{}, paymentOrderNo:{}", profitSharing.getProfitSharingNo(), item.getPaymentOrderNo());

        // 分润解冻lock
        String xydCreditPayProfitSharingLock = RedisUtil.PREFIX_XYD_CREDIT_PAY_PROFITSHARING_LOCK + item.getPaymentOrderNo();

        if (!redisUtil.tryLock(xydCreditPayProfitSharingLock)) {
            log.error("###小雨点支付分润 get payProfitSharingLock fail, {}-{}", profitSharing.getProfitSharingNo(), item.getPaymentOrderNo());
            // 可重试
            throw new PaymentException(ResultCodeEnum.PROFITSHARING_BUSY);
        }

        try {
            // 判断支付单是否已结算（有冻结成功记录）
            SeparateOrder freezeSeparateOrder = separateOrderService.queryOneByBusinessOrderNo(item.getPaymentOrderNo() + "_" + AmountTypeEnum.XYD_CREDIT_FREEZE.name());
            if (freezeSeparateOrder == null || !Objects.equals(freezeSeparateOrder.getStatus(), SeparateOrder.STATUS_FINISH)) {
                log.info("###小雨点支付分润 支付单未结算, profitSharingNo:{}, paymentOrderNo:{}", profitSharing.getProfitSharingNo(), item.getPaymentOrderNo());
                throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_SETTLE_CONTAIN_NOT_CLEARING_ORDER);
            }

            // 当前支付单已解冻金额
            List<RefundOrder> refundOrders = refundOrderService.selectListByPayNo(item.getPaymentOrderNo());
            Integer unfreezeAmount = getXydCreditUnFreezeAmt(item.getPaymentOrderNo(), refundOrders);

            // 剩余冻结金额
            int remainFreezeAmount = item.getOnlinePayAmount() - unfreezeAmount;

            log.info("###小雨点支付分润 {}-{} onlinePayAmount:{}, unfreezeAmount:{}, remainFreezeAmount:{}", profitSharing.getProfitSharingNo(), item.getPaymentOrderNo(),
                    item.getOnlinePayAmount(), unfreezeAmount, remainFreezeAmount);

            if (remainFreezeAmount > 0) {
                // 分润解冻: 卖家冻结——>卖家可提现
                AccountTransferDto accountTransferDto = AccountTransferDto.builder().
                        transferType(FrozenDto.FUCC_FLAG_6).
                        businessIdType(profitSharing.getBusinessOrderType()).
                        outAccountId(profitSharing.getAccountId()).
                        inAccountId(profitSharing.getAccountId()).
                        amount(remainFreezeAmount).
                        orderNo(item.getPaymentOrderNo() + "_" + AmountTypeEnum.XYD_CREDIT_FREEZE.name()).
                        businessOrderNo(item.getPaymentOrderNo() + "_" + AmountTypeEnum.XYD_CREDIT_PU.name()).
                        bizNo(item.getPaymentOrderNo()).
                        extra("分润解冻").build();
                RetryStrategy.RETRY_2_ATTEMPTS.executeWithRetry(() -> pinganAdapter.asyncUnfreezeMembershipAndPoll(accountTransferDto), RetryTradeType.XYD_CREDIT_PAY_PROFITSHARING, true);
            }

            // 垫资
            xydCreditProfitSharingAdvanceRefundOrder(refundOrders, profitSharing, item);

        } finally {
            redisUtil.unLock(xydCreditPayProfitSharingLock);
        }

    }

    private void xydCreditProfitSharingAdvanceRefundOrder(List<RefundOrder> refundOrders, ProfitSharing profitSharing, ProfitSharingDetail item) throws PaymentException {
        // 支付已结算金额
        Integer settledAmount = separateOrderService2.getSettledAmount(item.getPaymentOrderNo() + "_" + AmountTypeEnum.XYD_CREDIT_PU.name());
        int advanceAmount = item.getOnlinePayAmount() - settledAmount;
        if (advanceAmount > 0) {
            log.info("###小雨点支付分润 开始垫资 {}-{} onlinePayAmount:{}, settledAmount:{}", profitSharing.getProfitSharingNo(), item.getPaymentOrderNo(),
                    item.getOnlinePayAmount(), settledAmount);

            if (CollectionUtil.isEmpty(refundOrders)) {
                log.error("###小雨点支付分润 退款垫资时退款单不存在, {}-{}", profitSharing.getProfitSharingNo(), item.getPaymentOrderNo());
                throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_SETTLE_NOT_MATCH_REFUND_AMOUNT, item.getOrderNo() + "支付单分润垫资时退款单不存在（小雨点）" + item.getPaymentOrderNo());
            }
            // 已退总金额
            Integer totalRefundAmount = refundOrders.stream()
                    .filter(refundOrder -> RefundOrder.STATUS_FINISH.equals(refundOrder.getStatus()))
                    .mapToInt(RefundOrder::getAmount).sum();
            if (!Objects.equals(totalRefundAmount, advanceAmount)) {
                log.error("###小雨点支付分润 退款垫资时垫资金额不等于已退金额, {}-{}, totalRefundAmount:{}, advanceAmount:{}", profitSharing.getProfitSharingNo(), item.getPaymentOrderNo(),
                        totalRefundAmount, advanceAmount);
                throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_SETTLE_NOT_MATCH_REFUND_AMOUNT, item.getOrderNo() + "支付单分润垫资时退款金额不等于垫资金额（小雨点）" + item.getPaymentOrderNo());
            }

            // 垫资
            for (RefundOrder refundOrder : refundOrders) {
                // 记录separate_order -> (extra:会员间交易) 平安退款账户 -> 商家账户
                separateOrderService2.accountTransfer(AccountTransferDto.builder().
                        transferType(AccountTransferDto.TRANSFER_PAY).
                        businessIdType(profitSharing.getBusinessOrderType()).
                        outAccountId(ApolloUtil.getProperty(Constants.EC_PINGAN_REFUND_ACCOUNT_ID)).
                        inAccountId(profitSharing.getAccountId()).
                        amount(refundOrder.getAmount()).
                        businessOrderNo(refundOrder.getRefundNo()).
                        remark(refundOrder.getBusinessRefundNo() + "|" + "结算时-退款垫资").
                        build());
            }
        }

    }

    private Integer getXydCreditUnFreezeAmt(String paymentOrderNo, List<RefundOrder> refundOrders) {
        // 已解冻金额
        Integer unfreezeAmount = 0;

        // 支付分润解冻
        List<SeparateOrder> puSeparateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(paymentOrderNo + "_" + AmountTypeEnum.XYD_CREDIT_PU.name());
        if (CollectionUtil.isNotEmpty(puSeparateOrders)) {
            for (SeparateOrder puSeparateOrder : puSeparateOrders) {
                if (puSeparateOrder != null && Objects.equals(puSeparateOrder.getStatus(), SeparateOrder.STATUS_FINISH)) {
                    unfreezeAmount += puSeparateOrder.getTradeAmount();
                }
            }
        }

        // 退款解冻
        if (CollectionUtil.isNotEmpty(refundOrders)) {
            List<String> separateOrderBizNos = refundOrders.stream()
                    .map(r -> String.join("_", r.getRefundNo(), AmountTypeEnum.XYD_CREDIT_UNFREEZE.name()))
                    .collect(Collectors.toList());
            List<SeparateOrder> separateOrders = separateOrderService.selectListByBusinessOrderNos(separateOrderBizNos);
            if (CollectionUtil.isNotEmpty(separateOrders)) {
                for (SeparateOrder unfreezeSeparateOrder : separateOrders) {
                    if (unfreezeSeparateOrder != null && Objects.equals(unfreezeSeparateOrder.getStatus(), SeparateOrder.STATUS_FINISH)) {
                        unfreezeAmount += unfreezeSeparateOrder.getTradeAmount();
                    }
                }
            }
        }

        return unfreezeAmount;
    }

    /**
     * 富民结算/分润
     */
    private void profitSharingByFBank(ProfitSharing profitSharing) {
        String key = RedisUtil.PREFIX_PROFIT_SHARING + profitSharing.getAccountId();
        String withdrawKey = RedisUtil.D_AMOUNT_LOCK + profitSharing.getAccountId();

        try {
            if(!redisUtil.setNx(key)) {
                log.error("ProfitSharingService#profitSharingByFBank error[Repeat request], profitSharing={}", JSONObject.toJSONString(profitSharing));
                // 延迟10s重试
                callbackRunner.syncSendDelayMsg(RocketMqTopic.TOPIC_PROFIT_SHARING__CALLBACK, profitSharing, MqConstant.DELAY_LEVELS[NumberUtils.INTEGER_ONE]);
                return;
            }

            // 1.组装结算数据+账户金额校验
            List<SeparateAccountFromUserVo> separates = getOrder(profitSharing);

            // 2.结算
            // 2.1 解冻富民冻结金额+账户冻结金额校验
            unfrozenBalance(profitSharing);

            // 提现锁，可重入无需判断加锁返回值
            redisUtil.setNx(withdrawKey);

            // 2.2 降级金额转入商家可提现余额
            // 2.3 平台补贴金额转入商家可提现余额
            // 2.4 佣金回扣
            // 2.5 退款金额回扣
            for(SeparateAccountFromUserVo separate : separates) {
                if(separate.getAmount() <= 0) {
                    continue;
                }

                ResultVO<SeparateAccountsBo> resultVO = paymentOrderApi.separateAccountFromUser(separate, StringUtils.EMPTY);

                if(!ResultCodeEnum.SUCCESS.getCode().equals(resultVO.getCode())) {
                    throw new PaymentException(resultVO.getDisplayMsg());
                }
            }

            // 3.更新单据
            profitSharing.setStatus(SettlementStatusEnum.SUCCESS.getStatus());
            profitSharing.setMessage(SettlementStatusEnum.SUCCESS.getDesc());
            profitSharingMapper.updateByPrimaryKeySelective(profitSharing);

            // 4.释放提现锁
            redisUtil.del(withdrawKey);

            // 5.业务回调
            callbackRunner.callback(CallbackMsg.build(CallbackResultVo.PROFIT_SHARING_SUCCESS, profitSharing.getCallback(),
                    profitSharing.getProfitSharingNo(), profitSharing.getBusinessOrderNo(), ResultCodeEnum.SUCCESS.getDisplayMsg(), getCallBackExtend(profitSharing)));
        }
        catch(Exception ex) {
            log.error(ex.getMessage() + ", profitSharing=" + JSONUtils.toJSON(profitSharing), ex);
            String errorMsg = ex instanceof PaymentException ? ((PaymentException) ex).getErrMsg() : StringUtils.EMPTY;
            errorMsg = StringUtils.isNotBlank(errorMsg) ? errorMsg : SettlementStatusEnum.FAIL.getDesc();

            // 4.更新单据
            profitSharing.setStatus(SettlementStatusEnum.FAIL.getStatus());
            profitSharing.setMessage(errorMsg);
            profitSharingMapper.updateByPrimaryKeySelective(profitSharing);

            // 5.业务回调
            callbackRunner.callback(CallbackMsg.build(CallbackResultVo.PROFIT_SHARING_FAIL, profitSharing.getCallback(),
                    profitSharing.getProfitSharingNo(), profitSharing.getBusinessOrderNo(), errorMsg, getCallBackExtend(profitSharing)));
        }
        finally {
            redisUtil.del(key);
        }
    }

    /**
     * 结算幂等校验
     * @param profitsharingParam
     */
    private void idempotentCheck(ProfitSharingParam profitsharingParam) throws PaymentException {
        List<ProfitSharing> items = profitSharingMapper.queryByBusinessOrderNo(profitsharingParam.getBusinessOrderNo());

        if(!CollectionUtils.isEmpty(items)) {
            for(ProfitSharing item : items) {
                switch(SettlementStatusEnum.get(item.getStatus())) {
                    case PENDING:
                    case PROCESSING:
                        throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_PROCESSING);
                    case SUCCESS:
                        throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_SUCCESS);
                }
            }
        }
    }

    /**
     * 获取转账顺序
     * @param profitSharing
     * @return
     */
    private List<SeparateAccountFromUserVo> getOrder(ProfitSharing profitSharing) throws PaymentException {
        List<SeparateAccountFromUserVo> order = new ArrayList<>();

        if(BusinessOrderTypeEnum.EC_POP.getType().equals(profitSharing.getBusinessOrderType())) {
            // 1 降级金额转入商家可提现余额
            Integer directPayAmount = getChannelPayAmount(profitSharing, Constants.CHANNEL_DIRECT_NAME);

            if(directPayAmount > NumberUtils.INTEGER_ZERO) {
                SeparateAccountFromUserVo separateDirect = accountAdapter.buildSeparateAccount(profitSharing.getBusinessOrderType(), profitSharing.getAccountId(),
                        profitSharing.getBusinessOrderNo(), AmountTypeEnum.DIRECT_PAY_AMOUNT, directPayAmount);
                order.add(separateDirect);

                // 平台余额校验
                if(!checkBalance(profitSharing.getBusinessOrderType(), separateDirect.getBusinessUserIdOut(), separateDirect.getAmount(), Boolean.FALSE)) {
                    throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_SETTLE_BALANCE_PLATFORM);
                }
            }

            // 2 平台补贴金额转入商家可提现余额
            AmountDirectionType platform = accountAdapter.getAmountDirectionTyp(profitSharing.getBusinessOrderType(),
                    AmountTypeEnum.PLATFORM_DISCOUNT_AMOUNT, profitSharing.getTotalPlatformDiscountAmount());
            SeparateAccountFromUserVo separatePlatform = accountAdapter.buildSeparateAccount(profitSharing.getBusinessOrderType(), profitSharing.getAccountId(),
                    profitSharing.getBusinessOrderNo(), AmountTypeEnum.PLATFORM_DISCOUNT_AMOUNT, profitSharing.getTotalPlatformDiscountAmount());

            // 平台补贴余额校验
            if(platform == AmountDirectionType.ADD &&
                    !checkBalance(profitSharing.getBusinessOrderType(),
                            separatePlatform.getBusinessUserIdOut(),
                            separatePlatform.getAmount(), Boolean.FALSE))
            {
                throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_SETTLE_BALANCE_PLATFORM_DISCOUNT);
            }

            // 3 购物金/余额支付转入商家可提现余额
            Integer balancePayAmount = getChannelPayAmount(profitSharing, Constants.CHANNEL_BALANCE_NAME);

            if(balancePayAmount > NumberUtils.INTEGER_ZERO) {
                SeparateAccountFromUserVo separateBalance = accountAdapter.buildSeparateAccount(profitSharing.getBusinessOrderType(), profitSharing.getAccountId(),
                        profitSharing.getBusinessOrderNo(), AmountTypeEnum.BALANCE_PAY_AMOUNT, balancePayAmount);
                order.add(separateBalance);

                // 购物金余额校验
                if(!checkBalance(profitSharing.getBusinessOrderType(), separateBalance.getBusinessUserIdOut(), separateBalance.getAmount(), Boolean.FALSE)) {
                    throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_SETTLE_BALANCE_PAY_AMOUNT_LOW);
                }
            }

            // 4 佣金回扣
            AmountDirectionType brokerage = accountAdapter.getAmountDirectionTyp(profitSharing.getBusinessOrderType(),
                    AmountTypeEnum.BROKERAGE_AMOUNT, profitSharing.getTotalBrokerageAmount());
            SeparateAccountFromUserVo separateBrokerage = accountAdapter.buildSeparateAccount(profitSharing.getBusinessOrderType(), profitSharing.getAccountId(),
                    profitSharing.getBusinessOrderNo(), AmountTypeEnum.BROKERAGE_AMOUNT, profitSharing.getTotalBrokerageAmount());

            if(brokerage == AmountDirectionType.ADD &&
                    !checkBalance(profitSharing.getBusinessOrderType(),
                            separateBrokerage.getBusinessUserIdOut(),
                            separateBrokerage.getAmount(), Boolean.FALSE))
            {
                throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_SETTLE_BALANCE_BROKERAGE);
            }

            if(platform == brokerage || platform == AmountDirectionType.ADD) {
                order.add(separatePlatform);
                order.add(separateBrokerage);
            }
            else {
                order.add(separateBrokerage);
                order.add(separatePlatform);
            }

            // 5 退款金额回扣（现金/在线支付）
            if(!NumberUtils.INTEGER_ZERO.equals(profitSharing.getTotalOnlineRefundAmount())) {
                order.add(accountAdapter.buildSeparateAccount(profitSharing.getBusinessOrderType(), profitSharing.getAccountId(),
                        profitSharing.getBusinessOrderNo(), AmountTypeEnum.REFUND_AMOUNT, profitSharing.getTotalOnlineRefundAmount()));
            }

            // 6 退款金额回扣（购物金/余额）
            if(!NumberUtils.INTEGER_ZERO.equals(profitSharing.getTotalBalanceRefundAmount())) {
                order.add(accountAdapter.buildSeparateAccount(profitSharing.getBusinessOrderType(), profitSharing.getAccountId(),
                        profitSharing.getBusinessOrderNo(), AmountTypeEnum.BALANCE_REFUND_AMOUNT, profitSharing.getTotalBalanceRefundAmount()));
            }

            log.info("ProfitSharingService#getOrder, order={}", JSONObject.toJSONString(order));
            return order;
        }

        throw new PaymentException("Profit sharing account not configured with " + profitSharing.getBusinessOrderType());
    }

    /**
     * 获取解冻列表
     * @param profitSharing
     * @return
     */
    private void unfrozenBalance(ProfitSharing profitSharing) throws PaymentException {
        Integer fbankPayAmount = getChannelPayAmount(profitSharing, Constants.CHANNEL_FUMIN_BANK_NAME);

        if(fbankPayAmount <= NumberUtils.INTEGER_ZERO) {
            return;
        }

        // 检查冻结金额是否充足
        if(!checkBalance(profitSharing.getBusinessOrderType(),
                payAccountService.getBusinessUserIdByAccountId(profitSharing.getAccountId()), fbankPayAmount, Boolean.TRUE))
        {
            throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_SETTLE_BALANCE_FROZEN);
        }

        // 解冻
        unfrozenBalance(profitSharing.getBusinessOrderType(), profitSharing.getAccountId(), fbankPayAmount);
    }

    /**
     * 解冻指定金额
     */
    public void unfrozenBalance(String businessIdType, String accountId, Integer targetUnfrozenAmount) throws PaymentException {
        List<FrozenSettlement> items = frozenSettlementService.getFrozenSettlement(accountId,
                Lists.newArrayList(FrozenStatusEnum.FROZEN_SUCCESS, FrozenStatusEnum.SUB_UNFROZEN_SUCCESS));

        if(targetUnfrozenAmount > getTotalFrozenAmount(items)) {
            throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_SETTLE_BALANCE_UNFROZEN);
        }

        for(FrozenSettlement item : items) {
            if(NumberUtils.INTEGER_ZERO.equals(targetUnfrozenAmount)) {
                break;
            }

            int unfrozenAmount = AmountCastUtil.castYuan2Fen(item.getFrozenAmount()) - AmountCastUtil.castYuan2Fen(item.getUnfrozenAmount());
            String thawrozenFlag;
            String amount;

            if(targetUnfrozenAmount >= unfrozenAmount) {
                thawrozenFlag = FrozenStatusEnum.SUB_UNFROZEN_SUCCESS.getStatus().equals(item.getStatus()) ? UnfrozenDto.SUB : UnfrozenDto.ALL;
                amount = AmountCastUtil.castFen2Yuan(unfrozenAmount);
                targetUnfrozenAmount -= unfrozenAmount;
            }
            else {
                thawrozenFlag = UnfrozenDto.SUB;
                amount = AmountCastUtil.castFen2Yuan(targetUnfrozenAmount);
                targetUnfrozenAmount -= targetUnfrozenAmount;
            }

            // 解冻
            UnfrozenBo unfrozenBo = routeManager.unfrozenBalance(UnfrozenDto.build(businessIdType,
                    accountId, leafUtil.getSnowflakeId(), item.getFrozenFlowNo(), thawrozenFlag, amount));

            // 更新账单
            item.setUnfrozenAmount(AmountCastUtil.castFen2Yuan(AmountCastUtil.castYuan2Fen(amount) + AmountCastUtil.castYuan2Fen(item.getUnfrozenAmount())));
            item.setUnfrozenFlowNo(unfrozenBo.getFlowNo());
            item.setUnfrozenDate(new Date());
            item.setStatus(AmountCastUtil.equalsAmount(item.getFrozenAmount(), item.getUnfrozenAmount()) ?
                    FrozenStatusEnum.UNFROZEN_SUCCESS.getStatus() : FrozenStatusEnum.SUB_UNFROZEN_SUCCESS.getStatus());
            item.setMessage(AmountCastUtil.equalsAmount(item.getFrozenAmount(), item.getUnfrozenAmount()) ?
                    FrozenStatusEnum.UNFROZEN_SUCCESS.getDesc() : FrozenStatusEnum.SUB_UNFROZEN_SUCCESS.getDesc());

            frozenSettlementService.updateById(item);
        }
    }

    /**
     * 获取渠道支付金额
     * @param profitSharing
     * @return
     */
    private Integer getChannelPayAmount(ProfitSharing profitSharing, String channelFlag) {
        Map<String, Integer> groupBySum = JSONUtils.parse2Map(profitSharing.getChannelGroupAmount(), String.class, Integer.class);

        AtomicReference<Integer> directPayAmount = new AtomicReference<>(0);

        groupBySum.entrySet().forEach(item -> {
            if (Constants.CHANNEL_DIRECT_NAME.equals(channelFlag)) {
                if(item.getKey().contains(channelFlag) || Constants.thirdChannelSubmitPingan(item.getKey())) {
                    directPayAmount.updateAndGet(v -> v + item.getValue());
                }
            } else {
                if(item.getKey().contains(channelFlag)) {
                    directPayAmount.updateAndGet(v -> v + item.getValue());
                }
            }
        });

        return directPayAmount.get();
    }

    /**
     * 获取冻结总金额
     * @param items
     * @return
     */
    private Integer getTotalFrozenAmount(List<FrozenSettlement> items) {
        int totalAmount = 0;

        for(FrozenSettlement item : items) {
            // 可解冻金额 = 冻结金额 - 已解冻金额
            totalAmount += AmountCastUtil.castYuan2Fen(item.getFrozenAmount()) - AmountCastUtil.castYuan2Fen(item.getUnfrozenAmount());
        }

        return totalAmount;
    }

    /**
     * 金额校验
     * @param businessOrderType
     * @param businessId
     * @param targetAmount
     * @param isFrozenAmount
     * @throws PaymentException
     */
    private boolean checkBalance(String businessOrderType, String businessId,
                              Integer targetAmount, boolean isFrozenAmount) throws PaymentException
    {
        UserBo result = accountService.queryAccountByBusinessId(businessId,
                businessOrderType, AccountChannelEnum.AGGREGATE.getChannel());
        return isFrozenAmount ? result.getFrozenAmount() >= targetAmount : result.getAvailableBalance() > targetAmount;
    }

    /**
     * 根据平台 查询分润
     * @param businessOrderType
     * @param businessOrderNo
     * @return
     */
    public ProfitSharing queryByBusinessOrderNoAndType(String businessOrderType, String businessOrderNo) {
        return profitSharingMapper.queryByBusinessOrderNoAndBusinessOrderType(businessOrderType, businessOrderNo);
    }

    /**
     * 获取结算/分润回调扩展信息
     * @param profitSharing
     * @return
     */
    public String getCallBackExtend(ProfitSharing profitSharing) {
        JSONObject object = new JSONObject();
        object.put("totalDirectPayAmount", getChannelPayAmount(profitSharing, Constants.CHANNEL_DIRECT_NAME));
        object.put("totalFBankPayAmount", getChannelPayAmount(profitSharing, Constants.CHANNEL_FUMIN_BANK_NAME));
        object.put("totalPinganPayAmount", getChannelPayAmount(profitSharing, Constants.CHANNEL_PINGAN_NAME));
        object.put("totalBalancePayAmount", getChannelPayAmount(profitSharing, Constants.CHANNEL_BALANCE_NAME));

        object.put("totalOnlineRefundAmount", profitSharing.getTotalOnlineRefundAmount());
        object.put("totalBalanceRefundAmount", profitSharing.getTotalBalanceRefundAmount());

        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(profitSharing.getAccountId());

        if(Constants.PINGAN.equals(thirdUserInfo.getThirdType())) {
            object.put("profitSharingChannel", Constants.CHANNEL_PINGAN_NAME);
        }
        else {
            object.put("profitSharingChannel", Constants.CHANNEL_FUMIN_BANK_NAME);
        }

        return object.toJSONString();
    }

    /**
     * 获取最近一笔交易时间
     */
    private String getLatelyTradeDateFromProfitSharingDetail(List<ProfitSharingDetail> profitSharingDetails, String format) {
        if(CollectionUtils.isEmpty(profitSharingDetails)) {
            return null;
        }

        // 平安渠道
        List<ProfitSharingDetail> sortedPayDetails = profitSharingDetails.stream().filter(detail -> detail.getPayChannel().contains(Constants.PINGAN)).
                sorted((a, b) -> b.getPaymentTradeDate().compareTo(a.getPaymentTradeDate())).
                collect(Collectors.toList());

        if(CollectionUtils.isEmpty(sortedPayDetails)) {
            return StringUtils.EMPTY;
        }

        return ConcurrentSimpleDateFormatUtil.format(sortedPayDetails.get(NumberUtils.INTEGER_ZERO).getPaymentTradeDate(), format);
    }

    /**
     * 分账
     */
    private void settle(ProfitSharing profitSharing, ProfitSharingDetail item) throws PaymentException {
        //如果是平安贷
        if (Constants.CHANNEL_PINGAN_CREDIT.equals(item.getPayChannel())) {
            popCreditSettle(profitSharing,item);
            return;
        }
        // 订单冻结余额查询 KFEJZB6292：未到账金额(NoSquareAmt->bankUnsettledAmount)、已到账金额(SquareAmt->bankSettledAmount)、可分账金额(BranchAmt->platformUnsettledAmount)
        OrderAmountInfoVo orderAmountInfoVo = routeManager.queryOrderAmountInfo(OrderAmountInfoDto.builder().
                businessIdType(profitSharing.getBusinessOrderType()).
                accountId(profitSharing.getAccountId()).
                payNo(item.getPaymentOrderNo()).
                build());
        // 可分账金额(BranchAmt)
        Integer platformUnsettledAmount = AmountCastUtil.castYuan2Fen(orderAmountInfoVo.getPlatformUnsettledAmount());
        log.info("settle paymentOrderNo{} platformUnsettledAmount:{}",item.getPaymentOrderNo(),platformUnsettledAmount);
        if (settleCheckSwitch) {
            Integer totalRefund = refundOrderService.queryTotalRefundAmount(item.getPaymentOrderNo());
            if(!Objects.equals(totalRefund, item.getOnlinePayAmount() - platformUnsettledAmount)) {
                throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_SETTLE_NOT_MATCH_REFUND,item.getOrderNo()+"订单退款金额不等于垫资金额");
            }
        }

        // 订单剩余金额分账，若可分账金额存在，则对可分账金额进行分账处理
        if(platformUnsettledAmount > NumberUtils.INTEGER_ZERO) {
            // KFEJZB6271：分账，会记录分账流水 separate_order -> (extra:分账)
            separateOrderService2.settle(SettleOrderDto.builder().
                    businessIdType(profitSharing.getBusinessOrderType()).
                    accountId(profitSharing.getAccountId()).
                    orderNo(item.getPaymentOrderNo()).
                    orderDate(item.getPaymentTradeDate()).
                    totalAmount(platformUnsettledAmount).
                    build());
        }
        // 已结算金额：separate_order.tradeAmount(status = SeparateOrder.STATUS_FINISH)
        Integer settledAmount = separateOrderService2.getSettledAmount(item.getPaymentOrderNo());

        // 结算垫资：在线支付金额 - 已结算金额 > 0，则进行结算垫资
        if(item.getOnlinePayAmount() - settledAmount > NumberUtils.INTEGER_ZERO) {
            List<RefundOrder> refundOrders = refundOrderService.selectListByPayNo(item.getPaymentOrderNo());
            // 已退总金额 (refund_order)
            Integer totalRefundAmount = refundOrders.stream().filter(refundOrder -> RefundOrder.STATUS_FINISH.equals(refundOrder.getStatus())). mapToInt(RefundOrder::getAmount).sum();
            // 已退总金额 != 在线支付金额 - 已结算金额，若垫资金额 != 已退款金额，则不允许结算垫资
            if(!Objects.equals(totalRefundAmount, item.getOnlinePayAmount() - settledAmount)) {
                throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_SETTLE_NOT_MATCH_REFUND_AMOUNT,item.getOrderNo()+"订单退款金额不等于垫资金额");
            }
            // 结算垫资
            for(RefundOrder refundOrder : refundOrders) {
                // 记录separate_order -> (extra:会员间交易) 平安退款账户 -> 商家账户
                separateOrderService2.accountTransfer(AccountTransferDto.builder().
                    transferType(AccountTransferDto.TRANSFER_PAY).
                    businessIdType(profitSharing.getBusinessOrderType()).
                    outAccountId(ApolloUtil.getProperty(Constants.EC_PINGAN_REFUND_ACCOUNT_ID)).
                    inAccountId(profitSharing.getAccountId()).
                    amount(refundOrder.getAmount()).
                    businessOrderNo(refundOrder.getRefundNo()).
                    remark(refundOrder.getBusinessRefundNo() + "|" + "结算时-退款垫资").
                    build());
            }
        }
    }

    private void popCreditSettle(ProfitSharing profitSharing, ProfitSharingDetail item) throws PaymentException {
        SeparateOrder buyerSeparateOrder = separateOrderService.queryOneByBusinessOrderNo(item.getPaymentOrderNo());
        log.info("refundCreditPay pop payNo:{}, buyerSeparateOrder:{}", item.getPaymentOrderNo(), JSON.toJSONString(buyerSeparateOrder));
        // log.info("refundCreditPay pop payNo:{} buyerSeparateOrder:{} status:{}", item.getPaymentOrderNo(), buyerSeparateOrder != null?true:false,buyerSeparateOrder.getStatus());
        if (buyerSeparateOrder != null && "finish".equals(buyerSeparateOrder.getStatus())) {
            //1.必须有冻结成功记录
            List<SeparateOrder> separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(item.getPaymentOrderNo() + "_" + AmountTypeEnum.CREDIT_FREEZE.name());
            SeparateOrder separateOrder = separateOrders.stream().filter(x -> "finish".equals(x.getStatus())).findFirst().orElse(null);
            if (separateOrder == null) {
                throw new PaymentException(ResultCodeEnum.FROZEN_NOTEXIST,item.getOrderNo()+"冻结记录不存在无法进行解冻");
            }
            OrderAmountInfoDto orderBalanceQueryParam = OrderAmountInfoDto.builder().
                    businessIdType(profitSharing.getBusinessOrderType()).
                    accountId(profitSharing.getAccountId()).
                    payNo(separateOrder.getBusinessOrderNo()).
                    build();
            OrderAmountInfoVo orderAmountInfoVo = pinganAdapter.orderBalanceQuery(orderBalanceQueryParam);
            log.info("orderAmountInfoVo:{}",JSONObject.toJSONString(orderAmountInfoVo));
            //1. 先查下这个单有没有解冻过，有说明分账前退款了
            //先看有没有发生退款，退了多少
            // 可分账金额(BranchAmt)
            Integer remainAvailAmt = AmountCastUtil.castYuan2Fen(orderAmountInfoVo.getRemainAvailAmt());
            log.info("orderAmountInfoVo payNo:{} remainAvailAmt:{} orderAmountInfoVo:{}",item.getPaymentOrderNo(),remainAvailAmt,JSONObject.toJSONString(orderAmountInfoVo));
            //大于0说明还有钱没解冻
            if (remainAvailAmt > NumberUtils.INTEGER_ZERO) {
                AccountTransferDto frozenParam = AccountTransferDto.builder().
                        transferType(AccountTransferDto.TRANSFER_PAY_T1).
                        businessIdType(profitSharing.getBusinessOrderType()).
                        outAccountId(profitSharing.getAccountId()).
                        inAccountId(profitSharing.getAccountId()).
                        amount(remainAvailAmt).
                        orderNo(item.getPaymentOrderNo() + "_" + AmountTypeEnum.CREDIT_FREEZE.name()).
                        businessOrderNo(item.getPaymentOrderNo() + "_" + AmountTypeEnum.CREDIT_PU.name()).
                        bizNo(item.getPaymentOrderNo()).
                        extra("分润解冻").build();
                // 2、卖家冻结->非冻结6007
                separateOrderService2.membershipTrancheFreeze(frozenParam);
            }

            // 已结算金额：separate_order.tradeAmount(status = SeparateOrder.STATUS_FINISH)
            Integer settledAmount = separateOrderService2.getSettledAmount(item.getPaymentOrderNo() + "_" + AmountTypeEnum.CREDIT_PU.name());
            log.info("refundCreditPay pop payNo:{} settledAmount:{}",item.getPaymentOrderNo(),settledAmount);

            // 结算垫资：在线支付金额 - 已结算金额 > 0，则进行结算垫资
            if(item.getOnlinePayAmount() - settledAmount > NumberUtils.INTEGER_ZERO) {
                List<RefundOrder> refundOrders = refundOrderService.selectListByPayNo(item.getPaymentOrderNo());
                // 已退总金额 (refund_order)
                Integer totalRefundAmount = refundOrders.stream().filter(refundOrder -> RefundOrder.STATUS_FINISH.equals(refundOrder.getStatus())). mapToInt(RefundOrder::getAmount).sum();
                // 已退总金额 != 在线支付金额 - 已结算金额，若垫资金额 != 已退款金额，则不允许结算垫资
                if(!Objects.equals(totalRefundAmount, item.getOnlinePayAmount() - settledAmount)) {
                    throw new PaymentException(ResultCodeEnum.PROFIT_SHARING_SETTLE_NOT_MATCH_REFUND_AMOUNT,item.getOrderNo()+"订单退款金额不等于垫资金额");
                }
                // 结算垫资
                for(RefundOrder refundOrder : refundOrders) {
                    // 记录separate_order -> (extra:会员间交易) 平安退款账户 -> 商家账户
                    separateOrderService2.accountTransfer(AccountTransferDto.builder().
                            transferType(AccountTransferDto.TRANSFER_PAY).
                            businessIdType(profitSharing.getBusinessOrderType()).
                            outAccountId(ApolloUtil.getProperty(Constants.EC_PINGAN_REFUND_ACCOUNT_ID)).
                            inAccountId(profitSharing.getAccountId()).
                            amount(refundOrder.getAmount()).
                            businessOrderNo(refundOrder.getRefundNo()).
                            remark(refundOrder.getBusinessRefundNo() + "|" + "结算时-退款垫资").
                            build());
                }
            }
        }
    }

    public ResultVO pinganCreditProfitsharing(List<String> payNos) {
        for (String payNo : payNos) {
            try {
                pinganCreditPaySettle(payNo);
            } catch (Exception e) {
                log.error("pinganCreditProfitsharing payNo:{}", payNo,e);
            }
        }
        return ResultVO.createSuccess();
    }

    private void pinganCreditPaySettleV2(String payNo) throws PaymentException {
        log.info("ProfitSharingService#pinganCreditPaySettleV2 start, payNo={}", payNo);

        PayResultOrder resultOrder = payResultOrderService.selectByPayNoAndChannel(payNo, ChannelEnum.PINGAN_CREDIT.getCode());
        log.info("ProfitSharingService#pinganCreditPaySettleV2, resultOrder={}, payNo={}", JSON.toJSONString(resultOrder), payNo);
        if (resultOrder == null) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }
        String payerAccount = payAccountService.getAccountIdByBusiness(BusinessIdTypeEnum.EC.getType(), resultOrder.getPayer());
        if (payerAccount == null) {
            throw new PaymentException(ResultCodeEnum.USER_NOT_FOUND);
        }

        try {
            pinganAdapter.settleCreditPay(resultOrder, payerAccount);
        } catch (Exception e) {
            log.error("ProfitSharingService#pinganCreditPaySettleV2 error, payNo={}", payNo, e);
            DingTalkMessage dingTalkMessage = DingTalkMessage.builder()
                    .type("平安贷分账")
                    .method("pinganCreditPaySettleV2")
                    .msg("平安贷分账异常,有资损风险,请尽快处理 ")
                    .param("payNo:" + payNo).build();
            alarmService.alarm(dingTalkMessage);
        }
    }

    public void xydCreditRefundSettle(String refundNo) throws PaymentException {
        log.info("###小雨点退款结算 xydCreditRefundSettle start, refundNo={}", refundNo);

        RefundOrder refundOrder = refundOrderService.selectByRefundNo(refundNo);
        if (refundOrder == null) {
            throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
        }
        if (!Objects.equals(refundOrder.getStatus(), RefundOrder.STATUS_FINISH)) {
            throw new PaymentException(ResultCodeEnum.ORDER_REFUND_NOT_FINISH);
        }

        // 退款结算锁
        String xydCreditRefundSettleLockKey = RedisUtil.PREFIX_XYD_CREDIT_REFUND_SETTLE_LOCK + refundNo;
        if (!redisUtil.tryLock(xydCreditRefundSettleLockKey)) {
            log.error("###小雨点退款结算 tryRefundSettleLock fail, refundNo={}", refundNo);
            return;
        }

        try {
            // 已结算过滤
            String xydCreditRefundSettleSucKey = RedisUtil.PREFIX_XYD_CREDIT_REFUND_SETTLE_SUCCESS + refundNo;
            if (redisUtil.exists(xydCreditRefundSettleSucKey)) {
                log.error("###小雨点退款结算 已结算成功过滤 {}", refundNo);
                return;
            }

            // 结算
            processXydCreditRefundSettle(refundOrder);

            log.info("###小雨点退款结算 xydCreditRefundSettle success end, refundNo={}", refundNo);
            // 存结算成功标记
            redisUtil.set(xydCreditRefundSettleSucKey, Constants.XYD_CREDIT_PAY_JOB_OFFSET_SECONDS);

        } catch (Exception e) {
            log.error("###小雨点退款结算 xydCreditRefundSettle error, refundNo={}", refundNo, e);

            String errMsg = "失败";
            if (e instanceof PaymentException) {
                PaymentException bizEx = (PaymentException) e;
                errMsg = bizEx.getErrMsg();

                // 判断是否需要上游重试
                shouldRetryUpstream(bizEx);
            }

            DingTalkMessage dingTalkMessage = DingTalkMessage.builder()
                    .type("小雨点退款结算")
                    .method("xydCreditRefundSettle")
                    .msg("小雨点退款结算异常, errMsg:" + errMsg)
                    .param("refundNo:" + refundNo).build();
            alarmService.alarm(dingTalkMessage);

        } finally {
            redisUtil.unLock(xydCreditRefundSettleLockKey);
        }
    }

    private void shouldRetryUpstream(PaymentException bizEx) throws PaymentException {
        String errCode = bizEx.getErrCode();
        if (Objects.equals(errCode, ResultCodeEnum.PROFITSHARING_BUSY.getCode())
                || Objects.equals(errCode, ResultCodeEnum.REPEAT_REQUEST.getCode())) {
            // 上游重试
            throw bizEx;
        }
    }

    private void processXydCreditRefundSettle(RefundOrder refundOrder) throws PaymentException {
        boolean selfSupportBizOrderType = pinganAdapter.isSelfSupportBizOrderType(refundOrder.getUserId(), refundOrder.getBusinessOrderType());
        boolean popBizOrderType = pinganAdapter.isPopBizOrderType(refundOrder.getBusinessOrderType());
        if (selfSupportBizOrderType) {
            // 自营不需处理
            log.info("###小雨点退款结算 自营无需处理, refundNo:{}-{}", refundOrder.getRefundNo(), refundOrder.getUserId());
        } else if (popBizOrderType) {
            // POP
            processXydCreditRefundPopSettle(refundOrder);
        } else {
            log.error("###小雨点退款结算 unknown businessOrderType, refundNo:{}-{}", refundOrder.getRefundNo(), refundOrder.getBusinessOrderType());
            throw new PaymentException(ResultCodeEnum.UN_SUPPORT_METHOD, "未知业务订单类型");
        }
    }

    private void processXydCreditRefundPopSettle(RefundOrder refundOrder) throws PaymentException {
        log.info("###小雨点退款结算 refundPopSettle, refundNo:{}", refundOrder.getRefundNo());

        // 退款解冻、支付单分润解冻 lock
        String xydCreditPayProfitSharingLock = RedisUtil.PREFIX_XYD_CREDIT_PAY_PROFITSHARING_LOCK + refundOrder.getPayNo();
        if (!redisUtil.tryLock(xydCreditPayProfitSharingLock)) {
            log.error("###小雨点退款结算 get payProfitSharingLock fail, {}-{}", refundOrder.getRefundNo(), refundOrder.getPayNo());
            // 上游重试
            throw new PaymentException(ResultCodeEnum.PROFITSHARING_BUSY);
        }

        // 卖家提现lock
        String popReceiverWithdrawLockKey = null;

        try {
            // 判断正向单是否分润
            List<SeparateOrder> puSeparateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(refundOrder.getPayNo() + "_" + AmountTypeEnum.XYD_CREDIT_PU.name());
            boolean payOrderShared = CollectionUtil.isNotEmpty(puSeparateOrders) && SeparateOrder.STATUS_FINISH.equals(puSeparateOrders.get(0).getStatus());

            if (!payOrderShared) {
                log.info("###小雨点退款结算 正向单未分润，回补退款到平安收佣金户 {}-{}", refundOrder.getRefundNo(), refundOrder.getPayNo());
                popReceiverWithdrawLockKey = RedisUtil.D_AMOUNT_LOCK + refundOrder.getUserId();
                redisUtil.setNx(popReceiverWithdrawLockKey);
                // 回补退款到平安收佣金户
                refundAmountReturnPlatformAccount(refundOrder);
            } else {
                log.info("###小雨点退款结算 正向单已分润，标记回补退款到平台佣金户记录 {}-{}", refundOrder.getRefundNo(), refundOrder.getPayNo());
                // 已分润，标记REFUND_RETURN_BROKERAGE_AMOUNT
                SeparateOrder separateOrder = buildRefundReturnBrokerageSeparateOrder(refundOrder);
                separateOrderService.insert(separateOrder);
            }
        } finally {
            redisUtil.unLock(xydCreditPayProfitSharingLock);
            if (popReceiverWithdrawLockKey != null) {
                redisUtil.unLock(popReceiverWithdrawLockKey);
            }
        }
    }

    private SeparateOrder buildRefundReturnBrokerageSeparateOrder(RefundOrder refundOrder) {
        SeparateOrder returnBrokerageSeparateOrder = separateOrderService.queryOneByBusinessOrderNo(refundOrder.getRefundNo() + "_" + AmountTypeEnum.XYD_CREDIT_REFUND_RETURN_BROKERAGE_AMOUNT.name());
        if (returnBrokerageSeparateOrder != null) {
            log.info("###小雨点退款结算 {} 回补退款到佣金户记录已存在, separateOrder:{}", refundOrder.getRefundNo(), JSON.toJSONString(returnBrokerageSeparateOrder));
            return returnBrokerageSeparateOrder;
        }

        SeparateOrder separateOrder = new SeparateOrder();
        separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
        separateOrder.setCustNoIn(ApolloUtil.getProperty(Constants.EC_PINGAN_IN_BROKERAGE_ACCOUNT_ID));
        separateOrder.setCustMerchant(refundOrder.getUserId());
        separateOrder.setTradeAmount(refundOrder.getAmount());
        separateOrder.setSettleNo(org.apache.commons.lang3.StringUtils.EMPTY);
        separateOrder.setBusinessOrderNo(refundOrder.getRefundNo() + "_" + AmountTypeEnum.XYD_CREDIT_REFUND_RETURN_BROKERAGE_AMOUNT.name());
        separateOrder.setExtra("会员间交易");
        separateOrder.setRefund(SeparateOrder.REFUND_STATUS_NONE);
        separateOrder.setStatus(SeparateOrder.STATUS_CREATE);
        separateOrder.setRefundSettleNo(org.apache.commons.lang3.StringUtils.EMPTY);
        separateOrder.setCtime(new Date());
        separateOrder.setUtime(new Date());
        separateOrder.setTradeCode(FbankTradeCodeEnum.FTC_1.getCode());
        separateOrder.setBizNo(refundOrder.getRefundNo());
        separateOrder.setRemark("退款分润时回补退款到平台收佣金户");
        return separateOrder;
    }

    private void refundAmountReturnPlatformAccount(RefundOrder refundOrder) throws PaymentException {
        // 查正向单冻结记录
        String businessOrderNo = refundOrder.getPayNo() + "_" + AmountTypeEnum.XYD_CREDIT_FREEZE.name();
        SeparateOrder separateOrder = separateOrderService.queryOneByBusinessOrderNo(businessOrderNo);
        if (separateOrder == null || !Objects.equals(separateOrder.getStatus(), SeparateOrder.STATUS_FINISH)) {
            log.error("###小雨点退款结算 正向单未结算无冻结记录, refundNo:{}, payNo:{}", refundOrder.getRefundNo(), refundOrder.getPayNo());
            throw new PaymentException(ResultCodeEnum.ORDER_NONE_SETTLE);
        }

        // 退款解冻：卖家冻结——>卖家可提现
        AccountTransferDto accountTransferDto = AccountTransferDto.builder().
                transferType(FrozenDto.FUCC_FLAG_6).
                businessIdType(refundOrder.getBusinessOrderType()).
                outAccountId(refundOrder.getUserId()).
                inAccountId(refundOrder.getUserId()).
                amount(refundOrder.getAmount()).
                orderNo(refundOrder.getPayNo() + "_" + AmountTypeEnum.XYD_CREDIT_FREEZE.name()).
                businessOrderNo(refundOrder.getRefundNo() + "_" + AmountTypeEnum.XYD_CREDIT_UNFREEZE.name()).
                bizNo(refundOrder.getRefundNo()).
                extra("退款解冻").build();
        RetryStrategy.RETRY_2_ATTEMPTS.executeWithRetry(() -> pinganAdapter.asyncUnfreezeMembershipAndPoll(accountTransferDto), RetryTradeType.XYD_CREDIT_REFUND, true);

        // 回补退款到平安收佣金户
        AccountTransferDto xydLoanFundPayAcctTransDto = AccountTransferDto.builder().
                transferType(AccountTransferDto.TRANSFER_REFUND).
                businessIdType(refundOrder.getBusinessOrderType()).
                outAccountId(refundOrder.getUserId()).
                inAccountId(ApolloUtil.getProperty(Constants.EC_PINGAN_IN_BROKERAGE_ACCOUNT_ID)).
                amount(refundOrder.getAmount()).
                businessOrderNo(refundOrder.getRefundNo() + "_" + AmountTypeEnum.XYD_CREDIT_REFUND_RETURN_BROKERAGE_AMOUNT.name()).build();
        RetryStrategy.RETRY_2_ATTEMPTS.executeWithRetry(() -> pinganAdapter.asyncAccountTransferAndPoll(xydLoanFundPayAcctTransDto), RetryTradeType.XYD_CREDIT_REFUND_SETTLE, true);
    }

    /**
     * 上游：支付成功回调mq、job补偿
     * 小雨点支付结算
     */
    public void xydCreditPaySettle(String payNo) throws PaymentException {
        log.info("###小雨点支付结算 xydCreditPaySettle start, payNo={}", payNo);

        // 查待结算信息
        PayResultOrder resultOrder = payResultOrderService.selectByPayNoAndChannel(payNo, ChannelEnum.XYD_LOAN.getCode());
        if (resultOrder == null) {
            throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
        }
        String payerAccount = payAccountService.getAccountIdByBusiness(BusinessIdTypeEnum.EC.getType(), resultOrder.getPayer());
        if (payerAccount == null) {
            throw new PaymentException(ResultCodeEnum.USER_NOT_FOUND);
        }

        // 支付结算锁
        String xydCreditPayerAccountSettleLockKey = RedisUtil.PREFIX_XYD_CREDIT_PAYER_ACCOUNT_SETTLE_LOCK + payerAccount;

        if (!redisUtil.tryLock(xydCreditPayerAccountSettleLockKey)) {
            log.error("###小雨点支付结算 tryLock fail, payNo={}, payerAccount:{}", payNo, payerAccount);
            // 重试
            throw new PaymentException(ResultCodeEnum.REPEAT_REQUEST);
        }

        try {
            // 已结算过滤
            String xydCreditPaySettleSucKey = RedisUtil.PREFIX_XYD_CREDIT_PAY_SETTLE_SUCCESS + payNo;
            if (redisUtil.exists(xydCreditPaySettleSucKey)) {
                log.error("###小雨点支付结算 已结算成功过滤 {}", payNo);
                return;
            }

            // 结算
            processXydCreditFundSettle(resultOrder, payerAccount);

            log.info("###小雨点支付结算 xydCreditPaySettle success end, payNo={}", payNo);
            // 存结算成功标记
            redisUtil.set(xydCreditPaySettleSucKey, Constants.XYD_CREDIT_PAY_JOB_OFFSET_SECONDS);
        } catch (Exception e) {
            log.error("###小雨点支付结算 xydCreditPaySettle error, payNo={}", payNo, e);

            String errMsg = "失败";
            if (e instanceof PaymentException) {
                PaymentException bizEx = (PaymentException) e;
                errMsg = bizEx.getErrMsg();

                // 判断是否需要上游重试
                shouldRetryUpstream(bizEx);
            }

            DingTalkMessage dingTalkMessage = DingTalkMessage.builder()
                    .type("小雨点支付结算")
                    .method("xydCreditPaySettle")
                    .msg("小雨点支付结算异常, errMsg:" + errMsg)
                    .param("payNo:" + payNo).build();
            alarmService.alarm(dingTalkMessage);
        } finally {
            redisUtil.unLock(xydCreditPayerAccountSettleLockKey);
        }
    }

    private void processXydCreditFundSettle(PayResultOrder resultOrder, String payerAccount) throws PaymentException {
        if (StringUtils.isBlank(xydLoadFundAccountId) ||
                StringUtils.isBlank(payerAccount) ||
                StringUtils.isBlank(resultOrder.getReceiveId())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "小雨点结算相关账户不存在");
        }

        // 买家提现lock
        String payerWithdrawLockKey = RedisUtil.D_AMOUNT_LOCK + payerAccount;
        // 卖家提现lock
        String popReceiverWithdrawLockKey = RedisUtil.D_AMOUNT_LOCK + resultOrder.getReceiveId();

        try {
            xydCreditFundSettleAcquireWithdrawLock(resultOrder.getBusinessOrderType(), payerWithdrawLockKey, popReceiverWithdrawLockKey);

            // 1、小雨点资方账户 会员间交易至 买家
            AccountTransferDto xydLoanFundPayAcctTransDto = AccountTransferDto.builder().
                    transferType(AccountTransferDto.TRANSFER_X).
                    businessIdType(resultOrder.getBusinessOrderType()).
                    outAccountId(xydLoadFundAccountId).
                    inAccountId(payerAccount).
                    amount(resultOrder.getAmount()).
                    orderNo(resultOrder.getPayNo()).
                    businessOrderNo(resultOrder.getPayNo() + "_" + AmountTypeEnum.XYD_CREDIT_FUND_PAY_ACCT_TRANS.name()).build();
            RetryStrategy.RETRY_2_ATTEMPTS.executeWithRetry(() -> pinganAdapter.asyncAccountTransferAndPoll(xydLoanFundPayAcctTransDto), RetryTradeType.XYD_CREDIT_PAY_SETTLE, true);

            // 2、买家 会员间交易至 卖家
            AccountTransferDto payerPayAcctTransDto = AccountTransferDto.builder().
                    transferType(AccountTransferDto.TRANSFER_PAY).
                    businessIdType(resultOrder.getBusinessOrderType()).
                    outAccountId(payerAccount).
                    inAccountId(resultOrder.getReceiveId()).
                    amount(resultOrder.getAmount()).
                    businessOrderNo(resultOrder.getPayNo() + "_" + AmountTypeEnum.XYD_CREDIT_PAYER_PAY_ACCT_TRANS.name()).build();
            RetryStrategy.RETRY_2_ATTEMPTS.executeWithRetry(() -> pinganAdapter.asyncAccountTransferAndPoll(payerPayAcctTransDto), RetryTradeType.XYD_CREDIT_PAY_SETTLE, true);

            // 3.3 非自营冻结
            boolean selfSupportBizOrderType = pinganAdapter.isSelfSupportBizOrderType(resultOrder.getReceiveId(), resultOrder.getBusinessOrderType());
            if (!selfSupportBizOrderType) {
                AccountTransferDto frozenParam = AccountTransferDto.builder().
                        transferType(FrozenDto.FUCC_FLAG_5).
                        businessIdType(resultOrder.getBusinessOrderType()).
                        outAccountId(resultOrder.getReceiveId()).
                        inAccountId(resultOrder.getReceiveId()).
                        amount(resultOrder.getAmount()).
                        orderNo(resultOrder.getPayNo() + "_" + AmountTypeEnum.XYD_CREDIT_FREEZE.name()).
                        businessOrderNo(resultOrder.getPayNo() + "_" + AmountTypeEnum.XYD_CREDIT_FREEZE.name()).
                        bizNo(resultOrder.getPayNo()).
                        extra("冻结").build();
                RetryStrategy.RETRY_2_ATTEMPTS.executeWithRetry(() -> pinganAdapter.asyncUnfreezeMembershipAndPoll(frozenParam), RetryTradeType.XYD_CREDIT_PAY_SETTLE, true);
            }
        } finally {
            redisUtil.unLock(payerWithdrawLockKey);
            redisUtil.unLock(popReceiverWithdrawLockKey);
        }
    }

    private void xydCreditFundSettleAcquireWithdrawLock(String businessOrderType, String payerWithdrawLockKey, String popReceiverWithdrawLockKey) {
        // 提现锁，可重入无需判断加锁返回值
        redisUtil.setNx(payerWithdrawLockKey);
        if (BusinessOrderTypeEnum.EC_POP.getType().equals(businessOrderType)) {
            redisUtil.setNx(popReceiverWithdrawLockKey);
        }

    }

    /**
     * 平安信用支付结算给卖家（目前仅支持EC自营）
     */
    public void pinganCreditPaySettle(String payNo) throws PaymentException {
        if (Boolean.TRUE.equals(settleCreditPayV2Switch) && CollectionUtil.isNotEmpty(settleCreditPayV2GrayPayNos) && settleCreditPayV2GrayPayNos.contains(payNo)) {
            pinganCreditPaySettleV2(payNo);
            return;
        }

        log.info("ProfitSharingService#pinganCreditPaySettle start, payNo={}", payNo);

        PayResultOrder resultOrder = payResultOrderService.selectByPayNoAndChannel(payNo, ChannelEnum.PINGAN_CREDIT.getCode());
        log.info("ProfitSharingService#pinganCreditPaySettle, resultOrder={}, payNo={}", JSON.toJSONString(resultOrder), payNo);

        if(resultOrder == null) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }

        String payerAccount = payAccountService.getAccountIdByBusiness(BusinessIdTypeEnum.EC.getType(), resultOrder.getPayer());

        if(payerAccount == null) {
            throw new PaymentException(ResultCodeEnum.USER_NOT_FOUND);
        }

        String key = RedisUtil.PREFIX_PINGAN_CREDIT_PAY_SETTLE + payerAccount;

        try {
            if(!redisUtil.setNx(key)) {
                log.error("ProfitSharingService#pinganCreditPaySettle error[Repeat request], payerAccount={}", payerAccount);
                return;
            }

            String pinganCreditPaySettleSucKey = RedisUtil.PREFIX_PINGAN_CREDIT_PAY_SETTLE_SUCCESS + payNo;
            if (redisUtil.exists(pinganCreditPaySettleSucKey)) {
                log.info("###平安贷结算已结算过滤, {}", payNo);
                // 已处理过的过滤掉
                return;
            }

            boolean pinganCreditPaySettleSuccess = true;
            String payDate = DateUtil.format(resultOrder.getCtime(), DateUtil.DATEFORMATDAY_02);
            QueryChannelClearVo queryChannelClearVo = routeManager.queryChannelClear(QueryChannelClearDto.builder().
                    businessIdType(resultOrder.getBusinessOrderType()).
                    accountId(resultOrder.getReceiveId()).
                    startDate(payDate).
                    endDate(payDate).
                    build());
            log.info("ProfitSharingService#pinganCreditPaySettle, queryChannelClearVo={}, payNo={}", JSON.toJSONString(queryChannelClearVo), payNo);

            // 未清分，不结算
            if(!queryChannelClearVo.isClearFinish()) {
                log.info("ProfitSharingService#pinganCreditPaySettle, queryChannelClearVo.isClearFinish=false, payNo={}", payNo);
                return;
            }

            // 已清分，发起结算
            Date resultOrderCDate = null;

            try {
                resultOrderCDate = DateUtil.getDayFirstTime(resultOrder.getCtime());
            }
            catch(Exception e) {
                log.error("ProfitSharingService#pinganCreditPaySettle getDayFirstTime error", e);
                throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
            }

            OrderAmountInfoVo orderAmountInfoVo = routeManager.queryOrderAmountInfo(OrderAmountInfoDto.builder().
                    businessIdType(resultOrder.getBusinessOrderType()).
                    accountId(payerAccount).
                    payNo(resultOrder.getPayNo()).
                    build());
            log.info("ProfitSharingService#pinganCreditPaySettle before settle orderAmountInfoVo={}", JSON.toJSONString(orderAmountInfoVo));

            Integer platformUnsettledAmount = AmountCastUtil.castYuan2Fen(orderAmountInfoVo.getPlatformUnsettledAmount());

            // 订单剩余金额分账
            if(platformUnsettledAmount > NumberUtils.INTEGER_ZERO) {
                log.info("ProfitSharingService#pinganCreditPaySettle platformUnsettledAmount>0 need settle payNo={}", payNo);

                String withdrawKey = RedisUtil.D_AMOUNT_LOCK + payerAccount; // 事务处理失败不允许买家提现，将来做买家提现要判断此redis锁
                // 提现锁，可重入无需判断加锁返回值
                redisUtil.setNx(withdrawKey);

                // 1、订单分账到买家
                separateOrderService2.settle(SettleOrderDto.builder().
                        businessIdType(resultOrder.getBusinessOrderType()).
                        accountId(payerAccount).
                        orderNo(resultOrder.getPayNo()).
                        orderDate(resultOrderCDate).
                        totalAmount(platformUnsettledAmount).
                        build());

                AccountTransferDto param = AccountTransferDto.builder().
                        transferType(AccountTransferDto.TRANSFER_PAY).
                        businessIdType(resultOrder.getBusinessOrderType()).
                        outAccountId(payerAccount).
                        inAccountId(resultOrder.getReceiveId()).
                        amount(platformUnsettledAmount).
                        businessOrderNo(resultOrder.getPayNo() + "_" + AmountTypeEnum.PINGAN_CREDIT_PAY_ACCT_TRANS.name()).build();
                int maxRetryCount = 0;
                boolean isRetry = false;
                boolean isSuccess = false;
                do {
                    try {
                        Thread.sleep(1000);

                        // 2、买家会员间交易到卖家
                        separateOrderService2.accountTransfer(param);
                        isRetry = false;
                        maxRetryCount ++;
                        isSuccess = true;
                    } catch (Exception e) {
                        maxRetryCount ++;
                        isRetry = true;
                        log.error("pinganCreditPaySettle 买家会员间交易到卖家失败 {}",param.getBusinessOrderNo());
                    }
                    final List<SeparateOrder> separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(param.getBusinessOrderNo());
                    final long count = separateOrders.stream().filter(x -> "finish".equalsIgnoreCase(x.getStatus())).count();
                    if (count > 0) {
                        isRetry = false;
                        isSuccess = true;
                    }
                    if (maxRetryCount > pingAnCreditSettleMaxRetryCount) {
                        //合计6秒
                        isRetry = false;
                    }
                } while (isRetry);

                pinganCreditPaySettleSuccess = isSuccess;

                if (!resultOrder.getBusinessOrderType().contains(BusinessIdTypeEnum.EC_SELF_SUPPORT.getType())) {
                    Thread.sleep(3000);
                    AccountTransferDto frozenParam = AccountTransferDto.builder().
                            transferType("5").
                            businessIdType(resultOrder.getBusinessOrderType()).
                            outAccountId(resultOrder.getReceiveId()).
                            inAccountId(resultOrder.getReceiveId()).
                            amount(platformUnsettledAmount).
                            orderNo(resultOrder.getPayNo() + "_" + AmountTypeEnum.CREDIT_FREEZE.name()).
                            businessOrderNo(resultOrder.getPayNo() + "_" + AmountTypeEnum.CREDIT_FREEZE.name()).
                            bizNo(resultOrder.getPayNo()).
                            extra("冻结").build();
                    // 2、卖家资金冻结
                    separateOrderService2.membershipTrancheFreeze(frozenParam);
                }


                // 释放提现锁
                if(withdrawKey != null) {
                    redisUtil.del(withdrawKey);
                }
                if (!isSuccess) {
                    DingTalkMessage msg = DingTalkMessage.builder()
                            .type("平安贷分账")
                            .method("ProfitSharingService.pinganCreditPaySettle")
                            .msg("买家会员间交易到卖家异常,请尽快处理有资损风险,调用接口：/api/pingan/accountTransfer,RetryCount：" + maxRetryCount)
                            .param(JSONObject.toJSONString(param)).build();
                    alarmService.alarm(msg);
                }

            }
            else {
                log.info("pinganCreditPaySettle platformUnsettledAmount=0 no need settle payNo={}", payNo);
            }

            log.info("ProfitSharingService#pinganCreditPaySettle end, payNo={}", payNo);

            if (pinganCreditPaySettleSuccess) {
                redisUtil.set(pinganCreditPaySettleSucKey, Constants.PINGAN_CREDIT_PAY_JOB_OFFSET_DAY * 86400);
            }

        } catch (Exception e) {
            log.error("ProfitSharingService#pinganCreditPaySettle error, payNo={}", payNo, e);
            DingTalkMessage msg = DingTalkMessage.builder()
                    .type("平安贷分账")
                    .method("pinganCreditPaySettle")
                    .msg("平安贷分账异常,有资损风险,请尽快处理 ")
                    .param("payNo:"+payNo).build();
            alarmService.alarm(msg);
        } finally {
            redisUtil.del(key);
        }
    }
    /**
     * 京东融资采购支付结算给卖家
     */
    public void jdCreditPaySettle(String payNo) throws PaymentException {
        log.info("jdCreditPaySettle start, payNo={}", payNo);
        PayResultOrder resultOrder = payResultOrderService.selectByPayNoAndChannel(payNo, ChannelEnum.PINGAN_CREDIT.getCode());
        log.info("jdCreditPaySettle, resultOrder={}, payNo={}", JSON.toJSONString(resultOrder), payNo);

        if(resultOrder == null) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }

        String payerAccount = payAccountService.getAccountIdByBusiness(BusinessIdTypeEnum.EC.getType(), resultOrder.getPayer());

        if(payerAccount == null) {
            throw new PaymentException(ResultCodeEnum.USER_NOT_FOUND);
        }

        String key = RedisUtil.PREFIX_PINGAN_CREDIT_PAY_SETTLE + payerAccount;

        try {
            if(!redisUtil.setNx(key)) {
                log.error("ProfitSharingService#jdCreditPaySettle error[Repeat request], payerAccount={}", payerAccount);
                return;
            }


            String withdrawKey = RedisUtil.D_AMOUNT_LOCK + payerAccount; // 事务处理失败不允许买家提现，将来做买家提现要判断此redis锁
            // 提现锁，可重入无需判断加锁返回值
            redisUtil.setNx(withdrawKey);

            Date resultOrderCDate = DateUtil.getDayFirstTime(resultOrder.getCtime());
            // 1、订单分账到买家
            separateOrderService2.settle(SettleOrderDto.builder().
                    businessIdType(resultOrder.getBusinessOrderType()).
                    accountId(payerAccount).
                    orderNo(resultOrder.getPayNo()).
                    orderDate(resultOrderCDate).
                    totalAmount(resultOrder.getAmount()).
                    build());

            AccountTransferDto param = AccountTransferDto.builder().
                    transferType(AccountTransferDto.TRANSFER_PAY).
                    businessIdType(resultOrder.getBusinessOrderType()).
                    outAccountId(payerAccount).
                    inAccountId(resultOrder.getReceiveId()).
                    amount(resultOrder.getAmount()).
                    businessOrderNo(resultOrder.getPayNo() + "_" + AmountTypeEnum.PINGAN_CREDIT_PAY_ACCT_TRANS.name()).build();
            int maxRetryCount = 0;
            boolean isRetry = false;
            boolean isSuccess = false;
            do {
                try {
                    Thread.sleep(1000);

                    // 2、买家会员间交易到卖家
                    separateOrderService2.accountTransfer(param);
                    isRetry = false;
                    maxRetryCount ++;
                    isSuccess = true;
                } catch (Exception e) {
                    maxRetryCount ++;
                    isRetry = true;
                    log.error("jdCreditPaySettle 买家会员间交易到卖家失败 {}",param.getBusinessOrderNo());
                }
                final List<SeparateOrder> separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(param.getBusinessOrderNo());
                final long count = separateOrders.stream().filter(x -> "finish".equalsIgnoreCase(x.getStatus())).count();
                if (count > 0) {
                    isRetry = false;
                    isSuccess = true;
                }
                if (maxRetryCount > pingAnCreditSettleMaxRetryCount) {
                    //合计6秒
                    isRetry = false;
                }
            } while (isRetry);

            if (!resultOrder.getBusinessOrderType().contains(BusinessIdTypeEnum.EC_SELF_SUPPORT.getType())) {
                Thread.sleep(3000);
                AccountTransferDto frozenParam = AccountTransferDto.builder().
                        transferType("5").
                        businessIdType(resultOrder.getBusinessOrderType()).
                        outAccountId(resultOrder.getReceiveId()).
                        inAccountId(resultOrder.getReceiveId()).
                        amount(resultOrder.getAmount()).
                        orderNo(resultOrder.getPayNo() + "_" + AmountTypeEnum.CREDIT_FREEZE.name()).
                        businessOrderNo(resultOrder.getPayNo() + "_" + AmountTypeEnum.CREDIT_FREEZE.name()).
                        bizNo(resultOrder.getPayNo()).
                        extra("冻结").build();
                // 2、卖家资金冻结
                separateOrderService2.membershipTrancheFreeze(frozenParam);
            }


            // 释放提现锁
            if(withdrawKey != null) {
                redisUtil.del(withdrawKey);
            }
            if (!isSuccess) {
                DingTalkMessage msg = DingTalkMessage.builder()
                        .type("平安贷分账")
                        .method("ProfitSharingService.jdCreditPaySettle")
                        .msg("买家会员间交易到卖家异常,请尽快处理有资损风险,调用接口：/api/pingan/accountTransfer,RetryCount：" + maxRetryCount)
                        .param(JSONObject.toJSONString(param)).build();
                alarmService.alarm(msg);
            }

            log.info("ProfitSharingService#jdCreditPaySettle end, payNo={}", payNo);
        } catch (Exception e) {
            log.error("ProfitSharingService#jdCreditPaySettle error, payNo={}", payNo);
            DingTalkMessage msg = DingTalkMessage.builder()
                    .type("京东采购融资分账")
                    .method("jdCreditPaySettle")
                    .msg("京东采购融资分账异常,有资损风险,请尽快处理 ")
                    .param("payNo:"+payNo).build();
            alarmService.alarm(msg);
        } finally {
            redisUtil.del(key);
        }
    }
    public void retryProfitSharing(List<String> list) {
        for (String profitSharingNo : list) {
            log.info("请求数据：{}", profitSharingNo);
            ProfitSharing profitSharing = profitSharingMapper.queryByProfitSharingNo(profitSharingNo);
            log.info("retryProfitSharingStatus: {}", JSONObject.toJSON(profitSharing));
            if (profitSharing == null || profitSharing.getStatus() == 8) {
                log.warn("updateProfitSharingStatus profitSharingNo={} ", profitSharingNo);
                return;
            }
            callbackRunner.syncSendMsg(RocketMqTopic.TOPIC_PROFIT_SHARING__CALLBACK, profitSharing);

        }
    }
    /**
     * 重试所有待处理分润
     */
    public void retryAllProfit(Long minId,Integer status) {
        if (status == null) {
            status =16;
        }
        List<ProfitSharing> pendingList = profitSharingMapper.selectPendingList(minId,status);
        if (CollectionUtils.isEmpty(pendingList)) {
            log.info("retryAllProfit:无待处理分润单数据，不进行后续处理！");
            return;
        }
        log.info("retryAllProfit:首次查询待处理分润单，待处理数据量:{}", pendingList.size());
        for (ProfitSharing profitSharing : pendingList) {
            if (profitSharing == null || profitSharing.getStatus() == 8) {
                continue;
            }
            callbackRunner.syncSendMsg(RocketMqTopic.TOPIC_PROFIT_SHARING__CALLBACK, profitSharing);
        }
        log.info("retryAllProfit:处理待分润单数据结束......");
    }

}
